﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace MadLibrary.Net.Ndmp
{
    /// <summary>
    /// Holds and manages client for NDM protocol.
    /// </summary>
    public class Client
    {
        /// <summary>
        /// Gets or sets the service.
        /// </summary>
        /// <value>
        /// The service.
        /// </value>
        public IPEndPoint Service { get; set; }
        /// <summary>
        /// Gets or sets the client socket.
        /// </summary>
        /// <value>
        /// The client socket.
        /// </value>
        protected Socket client { get; set; }
        /// <summary>
        /// Initializes a new instance of the <see cref="Client"/> class.
        /// </summary>
        public Client()
        {
            this.Service = new IPEndPoint(IPAddress.Any, 0);
            this.client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Client"/> class.
        /// </summary>
        /// <param name="Service">Specifies end point of service.</param>
        public Client(IPEndPoint Service)
        {
            this.Service = Service;
            this.client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        /// <summary>
        /// Connects this instance.
        /// </summary>
        public void Connect()
        {
            this.client.Connect(this.Service);
        }
        /// <summary>
        /// Connects the specified service.
        /// </summary>
        /// <param name="Service">Specifies service which has to be connected.</param>
        public void Connect(IPEndPoint Service)
        {
            this.Service = Service;
            this.Connect();
        }
        /// <summary>
        /// Requests the specified request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Service's response.</returns>
        public Response Request(Request request)
        {
            Response res = new Response();
            this.client.Send(Encoding.ASCII.GetBytes(request.ToString()));
            byte[] buffer = new byte[short.MaxValue];
            int received = 0;
            while ((received = this.client.Receive(buffer)) == 0 || received == -1 || received < 1)
            { }
            string result = Encoding.ASCII.GetString(buffer, 0, received);
            (res as IParsable).Parse(result);
            return res;
        }
        /// <summary>
        /// Deletes the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>Service's response.</returns>
        public virtual Response Delete(string item)
        {
            return Request(new Request(NdmpCommand.Delete, "item"));
        }
        /// <summary>
        /// Updates the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>Service's response.</returns>
        public virtual Response Update(string item)
        {
            return this.Request(new Request(NdmpCommand.Update, item));
        }
        /// <summary>
        /// Selects the specified item from service.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>Service's response.</returns>
        public virtual Response Select(string item)
        {
            return this.Request(new Request(NdmpCommand.Select, item));
        }
        /// <summary>
        /// Puts the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>Service's response.</returns>
        public virtual Response Put(string item)
        {
            return this.Request(new Request(NdmpCommand.Put, item));
        }
        /// <summary>
        /// Disconnects this instance.
        /// </summary>
        public void Disconnect()
        {
            this.client.Shutdown(SocketShutdown.Both);
        }
        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="Client"/> is reclaimed by garbage collection.
        /// </summary>
        ~Client()
        {
            this.Disconnect();
        }
    }
}
